package com.rockwell.sniffyhunter;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

import net.wimpi.modbus.usbserial.SerialParameters;

import org.opencv.core.Point;

import com.rockwell.sniffyhunter.R;
import com.rockwell.sniffyhunter.modbus.ModbusSerialClient;
import com.rockwell.sniffyhunter.utils.DetectionIndex;
import com.rockwell.sniffyhunter.utils.ICollector;
import com.rockwell.sniffyhunter.utils.IDetector;
import com.rockwell.sniffyhunter.utils.IMazeSolver;
import com.rockwell.sniffyhunter.utils.IRobotLogger;
import com.rockwell.sniffyhunter.model.DropZoneDetector;
import com.rockwell.sniffyhunter.model.LeftFirstMazeSolver;
import com.rockwell.sniffyhunter.model.Maze;
import com.rockwell.sniffyhunter.model.MazeDetector;
import com.rockwell.sniffyhunter.model.MazeExitDetector;
import com.rockwell.sniffyhunter.model.RightFirstMazeSolver;
import com.rockwell.sniffyhunter.model.Robot;
import com.rockwell.sniffyhunter.model.RobotCommander;
import com.rockwell.sniffyhunter.model.RobotSensors;
import com.rockwell.sniffyhunter.model.ObstacleDetector;
import com.rockwell.sniffyhunter.model.TreasureCollector;
import com.rockwell.sniffyhunter.model.TreasureDetector;
import com.rockwell.sniffyhunter.views.MazeCameraView;
import com.rockwell.sniffyhunter.views.OnColorBlobDetectListener;
import com.rockwell.sniffyhunter.views.OnOrientationChangeListener;

import android.os.AsyncTask;
import android.os.Bundle;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.hardware.usb.UsbManager;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ScrollView;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;

public class MaintenanceActivity extends Activity {
	private Robot 				m_robot;
    private ModbusSerialClient 	m_modbusMaster;
	private MazeCameraView		m_mazeCameraView;
	private Executor 			m_executor;
	
	private IRobotLogger m_logger = new IRobotLogger() {

		@Override
		public void log(final String msg) {
			MaintenanceActivity.this.runOnUiThread(new Runnable(){
			    public void run(){
			    	//Toast.makeText(MaintenanceActivity.this, msg, Toast.LENGTH_SHORT).show();
			    	TextView logDisplay = (TextView)findViewById(R.id.text_log);
			    	logDisplay.append(msg + "\n");
			    }
			});
		}

		@Override
		public void log(final Exception ex) {
			MaintenanceActivity.this.runOnUiThread(new Runnable(){
			    public void run(){
			    	Toast.makeText(MaintenanceActivity.this, ex.getMessage(), Toast.LENGTH_SHORT).show();
			    }
			});
		}

		@Override
		public void onRobotPaused() {
		}

		@Override
		public void onRobotResumed() {
		}

		@Override
		public void onRobotIdle() {
		}
    };
			
	private OnOrientationChangeListener m_orientationListener = new OnOrientationChangeListener() {
        public void onOrientationChanged(float[] orientation) {
        	m_robot.setOrientation(orientation[0]);
        }
    };
    
	@SuppressLint("UseSparseArrays") @SuppressWarnings("serial")
    private Map<Integer, OnColorBlobDetectListener> m_colorDetectListeners = new HashMap<Integer, OnColorBlobDetectListener>() {{
    	// Treasure Detect Listener
    	put(DetectionIndex.TREASURE, new OnColorBlobDetectListener() {
	        public void onDetect(int index, boolean present, Point center, float radius, int length, int width) {
	        	m_robot.onDetect(index, present, center, radius, length, width);
	        }
	    });
	    
	    // Obstacle Detect Listener
    	put(DetectionIndex.OBSTACLE, new OnColorBlobDetectListener() {
	        public void onDetect(int index, boolean present, Point center, float radius, int length, int width) {
	        	m_robot.onDetect(index, present, center, radius, length, width);
	        }
	    });
	    
	    // Maze 1 Detect Listener
    	put(DetectionIndex.MAZE1, new OnColorBlobDetectListener() {
	        public void onDetect(int index, boolean present, Point center, float radius, int length, int width) {
	        	m_robot.onDetect(index, present, center, radius, length, width);
	        }
	    });
	    
	    // Maze 2 Detect Listener
    	put(DetectionIndex.MAZE2, new OnColorBlobDetectListener() {
	        public void onDetect(int index, boolean present, Point center, float radius, int length, int width) {
	        	m_robot.onDetect(index, present, center, radius, length, width);
	        }
	    });
	    
	    // Maze 3 Detect Listener
    	put(DetectionIndex.MAZE3, new OnColorBlobDetectListener() {
	        public void onDetect(int index, boolean present, Point center, float radius, int length, int width) {
	        	m_robot.onDetect(index, present, center, radius, length, width);
	        }
	    });
	    
	    // Drop Zone Detect Listener
    	put(DetectionIndex.DROPZONE, new OnColorBlobDetectListener() {
	        public void onDetect(int index, boolean present, Point center, float radius, int length, int width) {
	        	m_robot.onDetect(index, present, center, radius, length, width);
	        }
	    });
    	
    	// Maze Detect Listener
    	put(DetectionIndex.MAZE_EXIT, new OnColorBlobDetectListener() {
	        public void onDetect(int index, boolean present, Point center, float radius, int length, int width) {
	        	m_robot.onDetect(index, present, center, radius, length, width);
	        }
	    });
    };};

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_maintenance);
		
		enableButtons(true);
		
		m_mazeCameraView = (MazeCameraView) this.findViewById(R.id.maze_camera_view);
		m_mazeCameraView.setTeachMode(false);
		m_mazeCameraView.setOrientationChangeListener(m_orientationListener);
		m_mazeCameraView.setDetectionListener(m_colorDetectListeners);
		
		SerialParameters params = new SerialParameters();
		params.setPortName("COM1");
		params.setBaudRate(19200);
		params.setDatabits(8);
		params.setParity("None");
		params.setStopbits(1);
		params.setEncoding("ascii");
		params.setEcho(false);

		UsbManager usb = (UsbManager) getSystemService(Context.USB_SERVICE);
    	m_modbusMaster = new ModbusSerialClient(usb);
		
    	final ScrollView sv = (ScrollView)findViewById(R.id.scrollview_log);
    	TextView tv = (TextView)findViewById(R.id.text_log);
    	tv.addTextChangedListener(new TextWatcher() {

			@Override
			public void afterTextChanged(Editable arg0) {
				sv.fullScroll(ScrollView.FOCUS_DOWN);
			}

			@Override
			public void beforeTextChanged(CharSequence s, int start, int count,
					int after) {
				// TODO Auto-generated method stub
				
			}

			@Override
			public void onTextChanged(CharSequence s, int start, int before,
					int count) {
				// TODO Auto-generated method stub
				
			}
    	});
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.robot, menu);
		return true;
	}

	@SuppressLint("UseSparseArrays") 
	@Override
	public void onStart() {
		super.onStart();
		
		if (m_robot != null)
			return;
		
		try {
			m_executor = Executors.newFixedThreadPool(1);
	
			double width = SettingsActivity.getRobotWidth(this);
			double length = SettingsActivity.getRobotLength(this);
			
			int startZone = SettingsActivity.getRobotStartZone(this);
			
			RobotSensors sensors = new RobotSensors(m_modbusMaster);
			RobotCommander commander = new RobotCommander(m_modbusMaster);
			@SuppressWarnings("serial")
			Map<Integer, IDetector> colorDetectors = new HashMap<Integer, IDetector>() {
				{
					put(DetectionIndex.TREASURE, new TreasureDetector());
					put(DetectionIndex.OBSTACLE, new ObstacleDetector());
					put(DetectionIndex.MAZE1, new MazeDetector());
					put(DetectionIndex.MAZE2, new MazeDetector());
					put(DetectionIndex.MAZE3, new MazeDetector());
					put(DetectionIndex.DROPZONE, new DropZoneDetector());
					put(DetectionIndex.MAZE_EXIT, new MazeExitDetector());
				}
			};
			
			ICollector treasureCollector = new TreasureCollector();
			IMazeSolver leftFirstSolver = new LeftFirstMazeSolver();
			IMazeSolver rightFirstSolver = new RightFirstMazeSolver();
			leftFirstSolver.addSolver(rightFirstSolver);
			
			m_robot = new Robot(width, length,
					startZone,
					sensors, 
					commander, 
					new Maze(30, 5), 
					leftFirstSolver, 
					colorDetectors,
					treasureCollector);
			m_robot.setLogger(m_logger, 5);
			m_robot.PopulatePredefinedSolvers(this);
			// TODO: Start the robot here...
			//m_robot.start(false);
		}
		catch (Exception ex) {
            Toast.makeText(this, ex.getMessage(), Toast.LENGTH_SHORT).show();
		}
	}
	
	@Override
	public void onStop() {
		super.onStop();
		m_mazeCameraView.onDeactivated();
	}
	
	@Override
	public void onResume() {
    	try {
			m_modbusMaster.connect("COM1");
			m_robot.setUseCompassCompensationDuringTurn(true);
		} catch (Exception e) {
			Toast.makeText(this, "Failed to connect to the modbus slave", Toast.LENGTH_LONG).show();
		}
    	
		super.onResume();
		m_mazeCameraView.onActivated();
	}
	
	@Override
    public void onPause()
    {
		m_modbusMaster.disconnect();
        super.onPause();
        m_mazeCameraView.onDeactivated();
    }

	public void onTestRobotAction(View v) {
		Spinner robotActionSpinner = (Spinner)findViewById(R.id.spinner_robot_actions);

		EditText parameterEditText = (EditText)findViewById(R.id.editText_parameter);
		String parameterText = parameterEditText.getText().toString();
		
		final int robotAction = robotActionSpinner.getSelectedItemPosition();
		final int parameter = parameterText.isEmpty()? 0 : Integer.parseInt(parameterText);
		
		RobotTask robotTask = new RobotTask(MaintenanceActivity.this, new Runnable() {

			@Override
			public void run() {
				m_robot.testRobotAction(robotAction, parameter);
			}
		});
		
		robotTask.executeOnExecutor(m_executor);
	}
	
	public void onTestRobotSequence(View v) {
		Spinner robotSequenceSpinner = (Spinner)findViewById(R.id.spinner_robot_sequences);
		final String robotSequence = robotSequenceSpinner.getSelectedItem().toString();
		
		int tempIndex = -1;
		int endPosition = robotSequence.indexOf('.');
		
		if (endPosition != -1) {
			tempIndex = Integer.parseInt(robotSequence.substring(0, endPosition));
		}
		else {
			return;
		}
		
		final int testSequenceIndex = tempIndex;
		
		RobotTask robotTask = new RobotTask(MaintenanceActivity.this, new Runnable() {

			@Override
			public void run() {
				m_robot.testRobotSequence(testSequenceIndex);
			}
		});
		
		robotTask.executeOnExecutor(m_executor);
	}
	
	private void enableButtons(boolean enabled) {
		Button button = (Button)findViewById(R.id.button_test_robot_action);
		button.setEnabled(enabled);
		button = (Button)findViewById(R.id.button_test_robot_sequence);
		button.setEnabled(enabled);
	}
	
	static class RobotTask extends AsyncTask<Void, Void, String> {
		MaintenanceActivity m_activity;
		Runnable m_task;
		
		public RobotTask(MaintenanceActivity activity, Runnable task) {
			m_activity = activity;
			m_task = task;
		}
		
		@Override
		protected void onPreExecute() {
			m_activity.enableButtons(false);
		}
		
		@Override
		protected String doInBackground(Void... params) {
			try {
				m_task.run();
				
				return "Succeeded";
			}
			catch (Exception ex) {
				return "Failed. " + ex.getMessage();
			}
		}
		
		@Override
		protected void onPostExecute(String result) {
			m_activity.enableButtons(true);
			
			Toast.makeText(m_activity, result, Toast.LENGTH_LONG).show();
		}
	}
}
